use macroquad::prelude::*;
use std::collections::HashMap;

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
enum CellState {
    Yellow,
    Green,
    Lake,
    Village,
    Ruins,
    Empty
}

#[derive(Copy, Clone, Debug, PartialEq)]
struct Ktoolhu {
    startpoint: (f32,f32),
    endpoint: (f32,f32),
    progress: f32,
    active: bool,
}

impl Ktoolhu {
    fn new(startcell: &Cell,) -> Ktoolhu {
        let res = Ktoolhu{
            startpoint: startcell.coords(),
            endpoint: (0.,0.),
            progress:0.,
            active: false,
        };
        return res;
    }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
struct Cell {
    i: u8,
    j: u8,
    state: CellState,
    marked: bool,
}
impl Cell{
    fn highlight(&self) {
        let (cell_size_x, cell_size_y) = cell_size();
        draw_rectangle(
            self.i as f32 * cell_size_x, 
            self.j as f32 * cell_size_y, 
            cell_size_x,
            cell_size_y,
            BLACK);
    }
    fn coords(&self) -> (f32,f32){
        let (cell_size_x, cell_size_y) = cell_size();
        return (
            self.i as f32 * cell_size_x, 
            self.j as f32 * cell_size_y, 
            )
    }

    fn destroy(&mut self) -> Vec<Ktoolhu> {
        let mut k: Vec<Ktoolhu> = Vec::new();
        match self.state{
            CellState::Village => {
                self.state = CellState::Ruins;
            },
            CellState::Yellow => {
                self.state = CellState::Green;
            },
            CellState::Green => {
                self.state = CellState::Lake;
            },
            CellState::Lake => {
                let mut kk = Ktoolhu::new(&self);
                k.push(kk);
            },
            _ => {},
        }
        return k;


    }
    fn starspawn(&self, ktoolhoos: &mut Vec<Ktoolhu>,state: &mut [[Cell;GRID_SIZE_X as usize]]){
        match find_village(state){
            Some(village) => {
                ktoolhoos.push(Ktoolhu::new(self));
                village.marked = true;
            },
            None =>{}
        };
    }
}

const GRID_SIZE_X : u8 = 8;
const GRID_SIZE_Y : u8 = 8;
const GREEN_SCORE : u8 = 1;
const LAKE_SCORE : u8 = 5;
const AOE_RADIUS : f32 = 1.5;

fn cell_size() -> (f32,f32){
    return (
        screen_width() / (GRID_SIZE_X as f32),
        screen_height()/(GRID_SIZE_Y as f32)
        )
}


fn draw_cell(cell: &mut Cell, texture_map: &HashMap<CellState,Texture2D>){
    let (cell_size_x, cell_size_y) = cell_size();
    let texture_got: Option<&Texture2D> = texture_map.get(&cell.state);
    match texture_got{
        None => {} ,
        Some(texture) => draw_texture(
            *texture,
            (cell.i) as f32 * cell_size_x ,
            (cell.j) as f32 * cell_size_y ,
            WHITE,
            )
    }

}
fn draw_k(k: &Ktoolhu, texture: &Texture2D){
    if k.active{
        draw_texture(
            *texture,
            lerp(k.startpoint.0, k.endpoint.0, k.progress),
            lerp(k.startpoint.1, k.endpoint.1, k.progress),
            WHITE,
            )
    }
}

fn lerp(f1:f32,f2:f32,progress:f32) -> f32{
    return (f2-f1) * progress + f1;
}

fn get_cell_under_cursor (state: &[[Cell;GRID_SIZE_X as usize]]) -> &Cell {
    let mouse_position: Vec2 = mouse_position().into();
    let (cell_size_x, cell_size_y) = cell_size();
    let i = (mouse_position.x/cell_size_x).floor() as usize;
    let j = (mouse_position.y/cell_size_y).floor() as usize;
    return &state [j][i];

}

fn init_state(state: &mut [[Cell;GRID_SIZE_X as usize]]){
    let mut i = 0 as u8;
    let mut j = 0 as u8;
    for row in state.iter_mut()  {
        for cell in row.iter_mut() {
            cell.i = i;
            cell.j = j;
            cell.state = CellState::Yellow;
            i += 1;
        }
        i = 0;
        j += 1;
    }
    state[2][3].state = CellState::Village;
    state[5][2].state = CellState::Village;
    state[3][4].state = CellState::Village;
}


fn find_village(state: &mut [[Cell;GRID_SIZE_X as usize]]) -> Option<&mut Cell> {
    for row in state.iter_mut()  {
        for cell in row.iter_mut() {
            if cell.state == CellState::Village && cell.marked == false {
                return Some(cell);
            }
        }
    }
    return None;
}

fn draw_end(scorestring: &String){
        draw_rectangle(
            0., 
            0., 
            screen_width(),
            screen_height(),
            BLACK);
        draw_text(scorestring, 20.0, screen_height()/2., 35., GRAY);
    }
fn count_score(state: & [[Cell;GRID_SIZE_X as usize]]) -> u8 {
    let mut res: u8 = 0;
    res += count_cellstate(state, CellState::Green)*GREEN_SCORE;
    res += count_cellstate(state, CellState::Lake)*LAKE_SCORE;
    return res;
}
fn scorestring(state: & [[Cell;GRID_SIZE_X as usize]]) -> String {
    let greens: u8 = count_cellstate(state, CellState::Green);
    let lakes: u8 = count_cellstate(state, CellState::Lake);
    let greensum = greens * GREEN_SCORE;
    let lakesum = lakes * LAKE_SCORE;
    let score:u8 = lakesum + greensum;
    let scorestring = format!("Revived:{} x {} = {}, Lakes: {}x{}={}, Total: {} ",
                              greens,
                              GREEN_SCORE,
                              greensum,
                              lakes,
                              LAKE_SCORE,
                              lakesum,
                              score);
    return scorestring;
}


fn count_cellstate(state: & [[Cell;GRID_SIZE_X as usize]], cs: CellState) -> u8 {
    let mut res : u8 = 0;
    for row in state  {
        for cell in row {
            if cell.state == cs {
                res += 1;
            }
        }
    }
    return res;
}

fn aoe_cells<'a>(state: &'a [[Cell;GRID_SIZE_X as usize]], start_i:u8, start_j:u8) -> Vec<(u8,u8)> {
    let mut res = Vec::new();
    for row in state  {
        for c in row {
            let distance_i = (c.i as f32 - start_i as f32).abs();
            let distance_j = (c.j as f32 - start_j as f32).abs();
            let distance = (distance_i*distance_i+distance_j*distance_j).sqrt();
            if distance <= AOE_RADIUS {
                res.push((c.i,c.j));
            }
        }
    }
    return res;
}


#[macroquad::main("Heartless Autumn")]
async fn main() {
    let tree_yellow_tx: Texture2D = load_texture("src/sprites/tree_yellow.png").await.unwrap();
    let tree_green_tx: Texture2D = load_texture("src/sprites/tree_green.png").await.unwrap();
    let village_tx: Texture2D = load_texture("src/sprites/hooman_hus.png").await.unwrap();
    let ruins_tx: Texture2D = load_texture("src/sprites/ruin.png").await.unwrap();
    let lake_tx: Texture2D = load_texture("src/sprites/lake.png").await.unwrap();
    let k_tx: Texture2D = load_texture("src/sprites/KTOOOLHOO.png").await.unwrap();
    let hooman_tx: Texture2D = load_texture("src/sprites/hooman_yellow.png").await.unwrap();
    let mut state = [[Cell {i:0,j:0, state: CellState::Empty, marked: false}; GRID_SIZE_X as usize]; GRID_SIZE_Y as usize];
    let mut holding_hooman: bool = false;
    let mut the_end: bool = false;

    let mut texture_map = HashMap::new();
    texture_map.insert(CellState::Yellow,tree_yellow_tx);
    texture_map.insert(CellState::Green,tree_green_tx);
    texture_map.insert(CellState::Village,village_tx);
    texture_map.insert(CellState::Lake,lake_tx);
    texture_map.insert(CellState::Ruins,ruins_tx);

    init_state(&mut state);
    let mut cursor_i:u8 = 0;
    let mut cursor_j:u8 =0;
    let mut ktoolhoos: Vec<Ktoolhu> = Vec::new();
    let mut can_reset: bool = false;
    const RESET_COOLDOWN: u8 = 20;
    let mut reset_cooldown_timer: u8 = RESET_COOLDOWN;



    loop {
        let mouse_position: Vec2 = mouse_position().into();
        let mut can_burst: bool = false;
        let mut can_pick: bool = false;


        clear_background(RED);
        let mut aoe_coords: Vec<(u8,u8)> = Vec::new();

        if can_reset && reset_cooldown_timer !=0{
            reset_cooldown_timer -= 1;
        }
        
        if can_reset && is_mouse_button_down(MouseButton::Left) && reset_cooldown_timer == 0 {
            init_state(&mut state);
            holding_hooman = false;
            the_end = false;
            can_reset = false;
            reset_cooldown_timer = RESET_COOLDOWN;
            ktoolhoos = Vec::new();
        }


        {

            let current_cell = get_cell_under_cursor(&state);

            if !holding_hooman
                && current_cell.state == CellState::Village {
                    can_pick = true;
                }

            if can_pick && is_mouse_button_down(MouseButton::Left) {
                holding_hooman = true;
            }

            if holding_hooman && current_cell.state != CellState::Village{
                can_burst = true;
            }

            if can_burst || can_pick{ 
                current_cell.highlight();
            }

            // because mutability problems

            cursor_i = current_cell.i;
            cursor_j = current_cell.j;

            aoe_coords = aoe_cells(&state,cursor_i,cursor_j)
        }
        if can_burst {
            for c in &aoe_coords{
                state[c.1 as usize][c.0 as usize].highlight();
            }
        }


        for row in state.iter_mut()  {
            for cell in row.iter_mut() {
                draw_cell(cell,&texture_map);
            }
        }

        if can_burst && is_mouse_button_down(MouseButton::Left) {
            holding_hooman = false;
            for c in aoe_coords{
                ktoolhoos.append(&mut state[c.1 as usize][c.0 as usize].destroy());
            }

        }
        const KTOOOLHOO_SPD: f32 = 0.01;
        for k in ktoolhoos.iter_mut(){
            if !k.active{ 
                match find_village(&mut state){
                    Some(village) => {
                        k.endpoint = village.coords();
                        k.active = true;
                    }
                    None => {},
                }
            }else{
                if k.progress <= 1.{
                    k.progress += KTOOOLHOO_SPD;
                } else {
                    for row in state.iter_mut() {
                        for c in row.iter_mut() {
                            if c.coords() == k.endpoint{
                                c.destroy();
                                k.active = false;
                            }
                        }
                    }
                }
            }
        }
        for k in &ktoolhoos {
            draw_k(&k,&k_tx);
        }

        if holding_hooman {
            draw_texture(
                hooman_tx,
                mouse_position.x - hooman_tx.width() / 2.,
                mouse_position.y - hooman_tx.height() / 2.,
                WHITE,
                );
        }

        if count_cellstate(&state,CellState::Village) == 0 {
            the_end = true;
            can_reset = true;
            holding_hooman = false;
        }


        if the_end {
            draw_end(&scorestring(&state));
        } else {
            draw_text(&scorestring(&state), 20.0, 20.0, 30.0, DARKGRAY);
        }

        next_frame().await
    }
}
